package org.jackysoft.security.handler;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jackysoft.service.SecurityRoleService;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.expression.WebSecurityExpressionRoot;
import org.springframework.security.web.util.IpAddressMatcher;


public class CustomSecurityExpressionRoot extends SecurityExpressionRoot
		implements MethodSecurityExpressionOperations {

	Log LOGGER = LogFactory.getLog(CustomSecurityExpressionRoot.class);
	private SecurityRoleService acegiService;
	private Object filterObject;
	private Object returnObject;
	private Object target;
	private Set<String> userRoles;
	/*
	 * This is for emulating WebSecurityExpressionRoot
	 */
	public HttpServletRequest request;

	/**
	 * @param a
	 */
	public CustomSecurityExpressionRoot(Authentication a) {
		super(a);
	}

	public CustomSecurityExpressionRoot(Authentication a, FilterInvocation fi) {
		super(a);
		this.request = fi.getRequest();
	}

	/**
	 * Checks given roles against the given regex expression
	 * 
	 * @param regex
	 *            to match agains roles
	 * @return true if at least 1 authority matches the regex, otherwise false
	 */
	public boolean hasRegexRole(String regex) {
		WebSecurityExpressionRoot sf;
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("hasRegexRole: " + regex);
		}

		boolean found = false;

		Set<String> authorities = getCustomAuthoritySet();

		for (String authority : authorities) {

			if (authority.matches(regex)) {
				found = true;
				break;
			}

		}

		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("hasRegexRole returns " + found);
		}

		return found;
	}
	
	public boolean hasSuperRole(String role){
		if(acegiService==null){
			LOGGER.warn("field acegiService can not be null");
		}
		return true;
	}

	/**
	 * 
	 * @see org.springframework.security.web.access.expression.
	 *      WebSecurityExpressionRoot.hasIpAddress(String) Takes a specific IP
	 *      address or a range using the IP/Netmask (e.g. 192.168.1.0/24 or
	 *      202.24.0.0/14).
	 * 
	 * @param ipAddress
	 *            the address or range of addresses from which the request must
	 *            come.
	 * @return true if the IP address of the current request is in the required
	 *         range.
	 */
	public boolean hasIpAddress(String ipAddress) {
		return (new IpAddressMatcher(ipAddress).matches(request));
	}

	/**
	 * Note: this does not return hierchacal roles like
	 * org.springframework.security
	 * .access.expression.SecurityExpressionRoot.getAuthoritySet()
	 * 
	 * @return set of authorities
	 */
	private Set<String> getCustomAuthoritySet() {

		if (userRoles == null) {
			userRoles = new HashSet<String>();
			Collection<? extends GrantedAuthority> userAuthorities = authentication
					.getAuthorities();

			userRoles = AuthorityUtils.authorityListToSet(userAuthorities);
		}

		return userRoles;
	}

	public void setFilterObject(Object filterObject) {
		this.filterObject = filterObject;
	}

	public Object getFilterObject() {
		return filterObject;
	}

	public void setReturnObject(Object returnObject) {
		this.returnObject = returnObject;
	}

	public Object getReturnObject() {
		return returnObject;
	}

	/**
	 * Sets the "this" property for use in expressions. Typically this will be
	 * the "this" property of the {@code JoinPoint} representing the method
	 * invocation which is being protected.
	 * 
	 * @param target
	 *            the target object on which the method in is being invoked.
	 */
	void setThis(Object target) {
		this.target = target;
	}

	public Object getThis() {
		return target;
	}
	
	public void setAcegiService(SecurityRoleService acegiService) {
		this.acegiService = acegiService;
	}

	
}
